package com.gzhryc.jdbc;

import com.gzhryc.jdbc.annotaion.Column;
import com.gzhryc.jdbc.annotaion.Join;
import com.gzhryc.jdbc.annotaion.JoinField;
import com.gzhryc.jdbc.models.Parameters;
import com.gzhryc.jdbc.utils.ClassTools;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateFormatUtils;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * 修改条件
 *
 * @author 张浩
 * @version 1.0.6
 */
public class Conditions {

	private final static String AND = "AND";
	private final static String OR = "OR";

	private final static String DESC = "DESC";
	private final static String ASC = "ASC";

	private Class<?> clazz;
	protected Conditions parent;
	protected List<Criterion> criteria;
	protected List<Order> orders;

	public Conditions(Class<?> clazz) {
		this.clazz = clazz;
	}

	public Conditions() {
	}

	public Criterion or() {
		Criterion criterion = new Criterion(this, OR);
		return criterion.or();
	}

	public Criterion and() {
		Criterion criterion = new Criterion(this, AND);
		return criterion.and();
	}

	public boolean hasMapperClass() {
		return this.clazz != null;
	}

	/**
	 * 从小到大排序
	 *
	 * @param field
	 * @return
	 */
	public Conditions asc(String field) {
		if (orders == null) {
			orders = new ArrayList<Order>();
		}
		orders.add(new Order(field, ASC));
		return this;
	}

	/**
	 * 从大到小排序
	 *
	 * @return
	 */
	public Conditions desc(String field) {
		if (orders == null) {
			orders = new ArrayList<Order>();
		}
		orders.add(new Order(field, DESC));
		return this;
	}

	protected Criterion join(String type, boolean isNewGroup) {
		if (isNewGroup) {
			Criterion group = this.parent.join(type, false);
			Criterion criterion = new Criterion(group, type);
			group.criteria = new ArrayList<Criterion>();
			group.criteria.add(criterion);
			return criterion;
		} else {
			if (criteria == null) {
				criteria = new ArrayList<>();
			}
			Criterion criterion = new Criterion(this, type);
			criteria.add(criterion);
			return criterion;
		}
	}

	public String toSql(Parameters params, boolean hasWhere) {
		return toSql(params, hasWhere, true);
	}

	public String toSql(Parameters params, boolean hasWhere, boolean hasOrderBy) {
		StringBuilder sb = new StringBuilder("");
		if (criteria != null && criteria.size() > 0) {
			boolean hasJoin = !hasWhere;
			boolean isSet = false;
			StringBuilder where = new StringBuilder("");

			for (int i = 0; i < criteria.size(); i++) {
				String temp = criteria.get(i).toSql(clazz, params, hasJoin, (criteria.size() > 1));
				if (StringUtils.isNotBlank(temp)) {
					where.append(temp);
					hasJoin = true;
					isSet = true;
				}
			}

			if (isSet) {
				if (hasWhere) {
					sb.append(" WHERE ");
				} else {
					sb.append(" ");
				}
				sb.append(where);
			}
		}
		if (orders != null && hasOrderBy) {
			sb.append(" ORDER BY ");
			StringBuilder orderStr = new StringBuilder();
			for (Order order : orders) {
				String name = order.getField();
				if (!order.getField().contains(".")) {
					try {
						if (clazz != null) {
							Field field = ClassTools.getDeclaredField(clazz, order.getField());
							if (field != null) {
								Column column = field.getAnnotation(Column.class);
								if (column != null && StringUtils.isNotBlank(column.name())) {
									name = column.name();
								}
							}
						}
					} catch (SecurityException e) {
						e.printStackTrace();
					}
				}
				orderStr.append(",").append(name).append(" ").append(order.type);
			}
			sb.append(orderStr.substring(1));
		}
		return sb.toString();
	}

	public String toSql(Parameters params, String tableAlias, boolean hasWhere) {
		return toSql(params, tableAlias, hasWhere, true);
	}

	public String toSql(Parameters params, String tableAlias, boolean hasWhere, boolean hasOrderBy) {
		StringBuilder sb = new StringBuilder();
		if (criteria != null && criteria.size() > 0) {
			boolean hasJoin = !hasWhere;
			boolean isSet = false;
			StringBuilder where = new StringBuilder();

			for (int i = 0; i < criteria.size(); i++) {
				String temp = criteria.get(i).toSql(clazz, params, tableAlias, hasJoin, (criteria.size() > 1));
				if (StringUtils.isNotBlank(temp)) {
					where.append(temp);
					hasJoin = true;
					isSet = true;
				}
			}

			if (isSet) {
				if (hasWhere) {
					sb.append(" WHERE ");
				} else {
					sb.append(" ");
				}
				sb.append(where);
			}
		}
		if (orders != null && hasOrderBy) {
			sb.append(" ORDER BY ");
			StringBuilder orderStr = new StringBuilder();
			for (Order order : orders) {
				String name = order.getField();
				if (!order.getField().contains(".")) {
					name = tableAlias + "." + order.getField();
					try {
						if (clazz != null) {
							Field field = ClassTools.getDeclaredField(clazz, order.getField());
							if (field != null) {
								Column column = field.getAnnotation(Column.class);
								if (column != null && StringUtils.isNotBlank(column.name())) {
									name = tableAlias + "." + column.name();
								}
							}
						}
					} catch (SecurityException e) {
						e.printStackTrace();
					}
				}
				orderStr.append(",").append(name).append(" ").append(order.type);
			}
			sb.append(orderStr.substring(1));
		}
		return sb.toString();
	}

	public String toJoinSql(Parameters params, String tableAlias, boolean hasWhere) {
		return toJoinSql(params, tableAlias, hasWhere, true);
	}

	public String toJoinSql(Parameters params, String tableAlias, boolean hasWhere, boolean hasOrderBy) {
		StringBuilder sb = new StringBuilder();
		if (criteria != null && criteria.size() > 0) {
			boolean hasJoin = !hasWhere;
			boolean isSet = false;
			StringBuilder where = new StringBuilder();

			for (int i = 0; i < criteria.size(); i++) {
				String temp = criteria.get(i).toJoinSql(clazz, params, tableAlias, hasJoin, (criteria.size() > 1));
				if (StringUtils.isNotBlank(temp)) {
					where.append(temp);
					hasJoin = true;
					isSet = true;
				}
			}

			if (isSet) {
				if (hasWhere) {
					sb.append(" WHERE ");
				} else {
					sb.append(" ");
				}
				sb.append(where);
			}
		}
		if (orders != null && hasOrderBy) {
			sb.append(" ORDER BY ");
			StringBuilder orderStr = new StringBuilder();
			for (Order order : orders) {
				String name = order.getField();
				if (!order.getField().contains(".")) {
					name = tableAlias + "." + order.getField();
					try {
						if (clazz != null) {
							Field field = ClassTools.getDeclaredField(clazz, order.getField());
							if (field != null) {
								Column column = field.getAnnotation(Column.class);
								if (column != null) {
									if (StringUtils.isNotBlank(column.name())) {
										name = tableAlias + "." + column.name();
									}
								} else {
									JoinField joinField = field.getAnnotation(JoinField.class);
									if (joinField != null) {
										String tempAlias = joinField.toTable();
										if (StringUtils.isNotBlank(joinField.toTableAlias())) {
											tempAlias = joinField.toTableAlias();
										}
										name = tempAlias + "." + joinField.name();
									}
								}
							}
						}
					} catch (SecurityException e) {
						e.printStackTrace();
					}
				}
				orderStr.append(",").append(name).append(" ").append(order.type);
			}
			sb.append(orderStr.substring(1));
		}
		return sb.toString();
	}

	public String toOrderSql() {
		StringBuilder sb = new StringBuilder();
		if (orders != null) {
			sb.append(" ORDER BY ");
			StringBuilder orderStr = new StringBuilder();
			for (Order order : orders) {
				String name = order.getField();
				if (!order.getField().contains(".")) {
					try {
						if (clazz != null) {
							Field field = ClassTools.getDeclaredField(clazz, order.getField());
							if (field != null) {
								Column column = field.getAnnotation(Column.class);
								if (column != null && StringUtils.isNotBlank(column.name())) {
									name = column.name();
								}
							}
						}
					} catch (SecurityException e) {
						e.printStackTrace();
					}
				}
				orderStr.append(",").append(name).append(" ").append(order.type);
			}

		}
		return sb.toString();
	}

	public String toOrderSql(String tableAlias) {
		StringBuilder sb = new StringBuilder();
		if (orders != null) {
			sb.append(" ORDER BY ");
			StringBuilder orderStr = new StringBuilder();
			for (Order order : orders) {
				String name = order.getField();
				if (!order.getField().contains(".")) {
					name = tableAlias + "." + order.getField();
					try {
						if (clazz != null) {
							Field field = ClassTools.getDeclaredField(clazz, order.getField());
							if (field != null) {
								Column column = field.getAnnotation(Column.class);
								if (column != null) {
									if (StringUtils.isNotBlank(column.name())) {
										name = tableAlias + "." + column.name();
									}
								} else {
									JoinField joinField = field.getAnnotation(JoinField.class);
									if (joinField != null) {
										String tempAlias = joinField.toTable();
										if (StringUtils.isNotBlank(joinField.toTableAlias())) {
											tempAlias = joinField.toTableAlias();
										}
										name = tempAlias + "." + joinField.name();
									}
								}
							}
						}
					} catch (SecurityException e) {
						e.printStackTrace();
					}
				}
				orderStr.append(",").append(name).append(" ").append(order.type);
			}
			sb.append(orderStr.substring(1));
		}
		return sb.toString();
	}

	public static class Criterion extends Conditions {

		private String join;
		private String field;
		private String type;
		private Object value;
		private Object[] values;
		private String separator;
		private Boolean isDay = false;
		private Boolean isMonth = false;
		private Boolean isLikeMore = false;

		private Criterion(Conditions parent, String join) {
			this.parent = parent;
			this.join = join;
		}

		/**
		 * 大于等于
		 *
		 * @param field
		 * @param value
		 * @return
		 */
		public Criterion ge(String field, Object value) {
			if (value != null) {
				if (StringUtils.isNotBlank(this.field)) {
					parent.join(AND, false).ge(field, value);
				} else {
					this.field = field;
					this.value = value;
					this.type = ">=";
				}
			}
			return this;
		}

		/**
		 * 大于
		 *
		 * @param field
		 * @param value
		 * @return
		 */
		public Criterion gt(String field, Object value) {
			if (field != null && value != null) {
				if (StringUtils.isNotBlank(this.field)) {
					parent.join(AND, false).gt(field, value);
				} else {
					this.field = field;
					this.value = value;
					this.type = ">";
				}
			}
			return this;
		}

		/**
		 * 小于等于
		 *
		 * @param field
		 * @param value
		 * @return
		 */
		public Criterion le(String field, Object value) {
			if (field != null && value != null) {
				if (StringUtils.isNotBlank(this.field)) {
					parent.join(AND, false).le(field, value);
				} else {
					this.field = field;
					this.value = value;
					this.type = "<=";
				}
			}
			return this;
		}

		/**
		 * 小于
		 *
		 * @param field
		 * @param value
		 * @return
		 */
		public Criterion lt(String field, Object value) {
			if (field != null && value != null) {
				if (StringUtils.isNotBlank(this.field)) {
					parent.join(AND, false).lt(field, value);
				} else {
					this.field = field;
					this.value = value;
					this.type = "<";
				}
			}
			return this;
		}

		/**
		 * 不等于
		 *
		 * @param field
		 * @param value
		 * @return
		 */
		public Criterion not(String field, Object value) {
			if (field != null && value != null) {
				if (StringUtils.isNotBlank(this.field)) {
					parent.join(AND, false).not(field, value);
				} else {
					this.field = field;
					this.value = value;
					this.type = "<>";
				}
			}
			return this;
		}

		/**
		 * 等于
		 *
		 * @param field
		 * @param value
		 * @return
		 */
		public Criterion is(String field, Object value) {
			if (field != null && value != null && StringUtils.isNotBlank(value.toString())) {
				if (StringUtils.isNotBlank(this.field)) {
					parent.join(AND, false).is(field, value);
				} else {
					this.field = field;
					this.value = value;
					this.type = "=";
				}
			}
			return this;
		}

		public Criterion sameDay(String field, Object value) {
			if (field != null && value != null) {
				if (StringUtils.isNotBlank(this.field)) {
					Criterion criterion = parent.join(AND, false).is(field, value);
					criterion.isDay = true;
				} else {
					this.field = field;
					this.value = value;
					this.type = "=";
					this.isDay = true;
				}
			}
			return this;
		}

		public Criterion sameMonth(String field, Object value) {
			if (field != null && value != null) {
				if (StringUtils.isNotBlank(this.field)) {
					Criterion criterion = parent.join(AND, false).is(field, value);
					criterion.isMonth = true;
				} else {
					this.field = field;
					this.value = value;
					this.type = "=";
					this.isMonth = true;
				}
			}
			return this;
		}

		public Criterion isNull(String field) {
			if (StringUtils.isNotBlank(this.field)) {
				parent.join(AND, false).isNull(field);
			} else {
				this.field = field;
				this.value = null;
				this.type = "is null";
			}

			return this;
		}

		public Criterion isNotNull(String field) {
			if (StringUtils.isNotBlank(this.field)) {
				parent.join(AND, false).isNull(field);
			} else {
				this.field = field;
				this.value = null;
				this.type = "is not null";
			}

			return this;
		}

		public Criterion in(String field, List<?> values) {
			if (field != null && values != null && values.size() > 0) {
				if (StringUtils.isNotBlank(this.field) && !this.type.equals("in")) {
					parent.join(AND, false).in(field, values);
				} else {
					this.field = field;
					this.values = values.toArray();
					this.type = "in";
				}
			}
			return this;
		}

		public Criterion in(String field, Object[] values) {
			if (field != null && values != null && values.length > 0) {
				if (StringUtils.isNotBlank(this.field) && !this.type.equals("in")) {
					parent.join(AND, false).in(field, values);
				} else {
					this.field = field;
					this.values = values;
					this.type = "in";
				}
			}
			return this;
		}

		public Criterion notIn(String field, List<?> values) {
			if (field != null && values != null && values.size() > 0) {
				if (StringUtils.isNotBlank(this.field) && !this.type.equals("ont in")) {
					parent.join(AND, false).in(field, values);
				} else {
					this.field = field;
					this.values = values.toArray();
					this.type = "not in";
				}
			}
			return this;
		}

		public Criterion notIn(String field, Object[] values) {
			if (field != null && values != null && values.length > 0) {
				if (StringUtils.isNotBlank(this.field) && !this.type.equals("ont in")) {
					parent.join(AND, false).in(field, values);
				} else {
					this.field = field;
					this.values = values;
					this.type = "not in";
				}
			}
			return this;
		}

		/**
		 * 像什么
		 *
		 * @param field
		 * @param value
		 * @return
		 */
		public Criterion like(String field, Object value) {
			if (field != null && value != null && StringUtils.isNotBlank(value.toString())) {
				if (StringUtils.isNotBlank(this.field)) {
					parent.join(AND, false).like(field, value);
				} else {
					this.field = field;
					this.value = value;
					this.type = "LIKE";
				}
			}
			return this;
		}

		public Criterion likeBoth(String field, String value) {
			if (field != null && StringUtils.isNotBlank(value)) {
				if (StringUtils.isNotBlank(this.field)) {
					parent.join(AND, false).likeBoth(field, value);
				} else {
					this.field = field;
					this.value = "%" + value + "%";
					this.type = "LIKE";
				}
			}
			return this;
		}

		public Criterion likeRight(String field, String value) {
			if (field != null && StringUtils.isNotBlank(value)) {
				if (StringUtils.isNotBlank(this.field)) {
					parent.join(AND, false).likeBoth(field, value);
				} else {
					this.field = field;
					this.value = value + "%";
					this.type = "LIKE";
				}
			}
			return this;
		}

		public Criterion likeLeft(String field, String value) {
			if (field != null && StringUtils.isNotBlank(value)) {
				if (StringUtils.isNotBlank(this.field)) {
					parent.join(AND, false).likeBoth(field, value);
				} else {
					this.field = field;
					this.value = "%" + value;
					this.type = "LIKE";
				}
			}
			return this;
		}

		public Criterion like(String field, String value, String separator) {
			if (field != null && StringUtils.isNotBlank(value)) {
				if (StringUtils.isNotBlank(this.field)) {
					parent.join(AND, false).like(field, value, separator);
				} else {
					this.field = field;
					this.value = value;
					this.type = "LIKE";
					this.isLikeMore = true;
					this.separator = separator;
				}
			}
			return this;
		}

		public Criterion or() {
			return join(OR, true);
		}

		public Criterion and() {
			return join(AND, true);
		}

		public String toSql(Class<?> clazz, Parameters params, boolean hasJoin, boolean isGroup) {
			return toSql(clazz, params, null, hasJoin, isGroup);
		}

		public String toSql(Class<?> clazz, Parameters params, String tableAlias, boolean hasJoin, boolean isGroup) {
			StringBuilder sb = new StringBuilder();
			if (criteria != null && criteria.size() > 0) {
				int count = 0;
				boolean forHasJoin = false;
				StringBuilder where = new StringBuilder("");
				for (int i = 0; i < criteria.size(); i++) {
					String temp = criteria.get(i).toSql(clazz, params, tableAlias, forHasJoin, true);
					if (StringUtils.isNotBlank(temp)) {
						where.append(temp);
						forHasJoin = true;
						count++;
					}
				}

				if (count > 0) {
					if (hasJoin) {
						sb.append(this.join);
					}
					if (count > 1 && isGroup) {
						sb.append(" (");
						sb.append(where);
						sb.append(") ");
					} else {
						sb.append(" ").append(where);
					}
				}
			} else if (StringUtils.isNotBlank(field)) {
				String name = this.field;
				Column column = null;
				if (clazz != null && !name.contains(".")) {
					try {
						Field field = ClassTools.getDeclaredField(clazz, this.field);
						if (field != null) {
							name = this.field;
							column = field.getAnnotation(Column.class);
							if (column != null && StringUtils.isNotBlank(column.name())) {
								name = column.name();
							}
						}
					} catch (Exception e) {
						// 忽略
					}
				}

				if (StringUtils.isNotBlank(tableAlias) && !name.contains(".")) {
					name = tableAlias + "." + name;
				}

				if (hasJoin) {
					sb.append(this.join);
					sb.append(" ");
				}

				if (this.type.equals("is not null") || this.type.equals("is null")) {
					sb.append(name);
					sb.append(" ");
					sb.append(this.type).append(" ");
				} else if (this.type.equals("in") || this.type.equals("not in")) {
					StringBuilder sb1 = new StringBuilder();
					for (Object item : this.values) {
						if(item != null) {
							sb1.append(",?");
							params.setObject(item);
						}
					}
					sb.append(name);
					sb.append(" ");
					sb.append(this.type);
					sb.append(" (");
					sb.append(sb1.substring(1));
					sb.append(") ");
				} else if (this.isDay) {
					sb.append("date_format(").append(name).append(",'%Y%m%d')");
					sb.append(" ");
					sb.append(this.type);
					sb.append(" ? ");
					if (this.value instanceof Date) {
						params.setObject(DateFormatUtils.format((Date) this.value, "yyyyMMdd"));
					} else {
						params.setObject(this.value);
					}
				} else if (this.isMonth) {
					sb.append("date_format(").append(name).append(",'%Y%m')");
					sb.append(" ");
					sb.append(this.type);
					sb.append(" ? ");
					if (this.value instanceof Date) {
						params.setObject(DateFormatUtils.format((Date) this.value, "yyyyMM"));
					} else {
						params.setObject(this.value);
					}
				} else if (this.isLikeMore) {
					sb.append("(");

					sb.append(name);
					sb.append(" ");
					sb.append(this.type);
					sb.append(" ? ");
					params.setObject(this.value);

					sb.append(" or ");

					sb.append(name);
					sb.append(" ");
					sb.append(this.type);
					sb.append(" ? ");
					params.setObject(this.value + this.separator + "%");

					sb.append(" or ");

					sb.append(name);
					sb.append(" ");
					sb.append(this.type);
					sb.append(" ? ");
					params.setObject("%" + this.separator + this.value + this.separator + "%");

					sb.append(" or ");

					sb.append(name);
					sb.append(" ");
					sb.append(this.type);
					sb.append(" ? ");
					params.setObject("%" + this.separator + this.value);

					sb.append(") ");
				} else if (this.value instanceof Date && column != null) {
					sb.append(name);
					sb.append(" ");
					sb.append(this.type);
					sb.append(" ? ");
					if (StringUtils.isNotBlank(column.type()) && "DATE".equals(column.type().toUpperCase())) {
						params.setObject(DateFormatUtils.format((Date) this.value, "yyyy-MM-dd"));
					} else {
						params.setObject(this.value);
					}
				} else {
					sb.append(name);
					sb.append(" ");
					sb.append(this.type);
					sb.append(" ? ");
					params.setObject(this.value);
				}
			}
			return sb.toString();
		}

		public String toJoinSql(Class<?> clazz, Parameters params, String tableAlias, boolean hasJoin, boolean isGroup) {
			StringBuilder sb = new StringBuilder();
			if (criteria != null && criteria.size() > 0) {
				int count = 0;
				boolean forHasJoin = false;
				StringBuilder where = new StringBuilder();
				for (int i = 0; i < criteria.size(); i++) {
					String temp = criteria.get(i).toJoinSql(clazz, params, tableAlias, forHasJoin, true);
					if (StringUtils.isNotBlank(temp)) {
						where.append(temp);
						forHasJoin = true;
						count++;
					}
				}

				if (count > 0) {
					if (hasJoin) {
						sb.append(this.join);
					}
					if (count > 1 && isGroup) {
						sb.append(" (");
						sb.append(where);
						sb.append(") ");
					} else {
						sb.append(" ").append(where);
					}
				}
			} else if (StringUtils.isNotBlank(field)) {
				String name = this.field;
				Column column = null;
				if (clazz != null && !name.contains(".")) {
					try {
						Field field = ClassTools.getDeclaredField(clazz, this.field);
						if (field != null) {
							name = tableAlias + "." + this.field;
							column = field.getAnnotation(Column.class);
							if (column != null) {
								if (StringUtils.isNotBlank(column.name())) {
									name = tableAlias + "." + column.name();
								}
							} else {
								JoinField joinField = field.getAnnotation(JoinField.class);
								if (joinField != null) {
									String tempAlias = joinField.toTable();
									if (StringUtils.isNotBlank(joinField.toTableAlias())) {
										tempAlias = joinField.toTableAlias();
									}
									name = tempAlias + "." + joinField.name();
								}
							}
						}
					} catch (Exception e) {
						// 忽略
					}
				}

				if (!name.contains(".")) {
					name = tableAlias + "." + name;
				}

				if (hasJoin) {
					sb.append(this.join);
					sb.append(" ");
				}

				if (this.type.equals("is not null") || this.type.equals("is null")) {
					sb.append(name);
					sb.append(" ");
					sb.append(this.type).append(" ");
				} else if (this.type.equals("in") || this.type.equals("not in")) {
					StringBuilder sb1 = new StringBuilder();
					for (Object item : this.values) {
						sb1.append(",?");
						params.setObject(item);
					}
					sb.append(name);
					sb.append(" ");
					sb.append(this.type);
					sb.append(" (");
					sb.append(sb1.substring(1));
					sb.append(") ");
				} else if (this.isDay) {
					sb.append("date_format(").append(name).append(",'%Y%m%d')");
					sb.append(" ");
					sb.append(this.type);
					sb.append(" ? ");
					if (this.value instanceof Date) {
						params.setObject(DateFormatUtils.format((Date) this.value, "yyyyMMdd"));
					} else {
						params.setObject(this.value);
					}
				} else if (this.isMonth) {
					sb.append("date_format(").append(name).append(",'%Y%m')");
					sb.append(" ");
					sb.append(this.type);
					sb.append(" ? ");
					if (this.value instanceof Date) {
						params.setObject(DateFormatUtils.format((Date) this.value, "yyyyMM"));
					} else {
						params.setObject(this.value);
					}
				} else if (this.isLikeMore) {
					sb.append("(");

					sb.append(name);
					sb.append(" ");
					sb.append(this.type);
					sb.append(" ? ");
					params.setObject(this.value);

					sb.append(" or ");

					sb.append(name);
					sb.append(" ");
					sb.append(this.type);
					sb.append(" ? ");
					params.setObject(this.value + this.separator + "%");

					sb.append(" or ");

					sb.append(name);
					sb.append(" ");
					sb.append(this.type);
					sb.append(" ? ");
					params.setObject("%" + this.separator + this.value + this.separator + "%");

					sb.append(" or ");

					sb.append(name);
					sb.append(" ");
					sb.append(this.type);
					sb.append(" ? ");
					params.setObject("%" + this.separator + this.value);

					sb.append(") ");
				} else if (this.value instanceof Date && column != null) {
					sb.append(name);
					sb.append(" ");
					sb.append(this.type);
					sb.append(" ? ");
					if (StringUtils.isNotBlank(column.type()) && "DATE".equals(column.type().toUpperCase())) {
						params.setObject(DateFormatUtils.format((Date) this.value, "yyyy-MM-dd"));
					} else {
						params.setObject(this.value);
					}
				} else {
					sb.append(name);
					sb.append(" ");
					sb.append(this.type);
					sb.append(" ? ");
					params.setObject(this.value);
				}
			}
			return sb.toString();
		}

		public String getField() {
			return field;
		}

		public Object getValue() {
			return value;
		}
	}

	public static class Order {

		private String field;
		private String type;

		public Order(String field, String type) {
			this.field = field;
			this.type = type;
		}

		public String getField() {
			return field;
		}

		public void setField(String field) {
			this.field = field;
		}

		public String getType() {
			return type;
		}

		public void setType(String type) {
			this.type = type;
		}
	}

	public static <T> Conditions build(T entity) {
		Conditions condition = new Conditions(entity.getClass());
		Field[] fields = ClassTools.getDeclaredFields(entity.getClass());
		for (int i = 0; i < fields.length; i++) {
			Field field = fields[i];
			field.setAccessible(true);
			try {
				Column column = field.getAnnotation(Column.class);
				if (column != null) {
					Object obj = field.get(entity);
					if (obj != null) {
						String name = field.getName();
						if (StringUtils.isNotBlank(column.name())) {
							name = column.name();
						}
						if (field.getType().equals(String.class) && (column.length() > 8 || name.endsWith("name"))) {
							condition.and().likeBoth(name, obj.toString());
						} else if (field.getType().equals(Date.class) && StringUtils.isNotBlank(column.type()) && "date".equals(column.type().toLowerCase())) {
							condition.and().sameDay(name, obj);
						} else {
							condition.and().is(name, obj);
						}
					}
				} else {
					Join join = field.getAnnotation(Join.class);
					if (join != null) {
						Object obj = field.get(entity);
						if (obj != null) {
							Field[] childFields = ClassTools.getDeclaredFields(field.getType());
							for (int j = 0; j < childFields.length; j++) {
								Field childField = childFields[j];
								childField.setAccessible(true);
								try {
									Column childColumn = childField.getAnnotation(Column.class);
									if (childColumn != null) {
										Object childObj = childField.get(obj);
										if (childObj != null) {
											String name = childField.getName();
											if (StringUtils.isNotBlank(childColumn.name())) {
												name = childColumn.name();
											}
											if (childField.getType().equals(String.class) && (childColumn.length() > 8 || name.endsWith("name"))) {
												condition.and().likeBoth(field.getName() + "." + name, childObj.toString());
											} else {
												condition.and().is(field.getName() + "." + name, childObj);
											}
										}
									}
								} catch (IllegalArgumentException | IllegalAccessException e) {
									e.printStackTrace();
								}
							}
						}
					} else {
						JoinField joinField = field.getAnnotation(JoinField.class);
						if (joinField != null) {
							Object obj = field.get(entity);
							if (obj != null) {
								String alias = joinField.toTable();
								if (StringUtils.isNoneBlank(joinField.toTableAlias())) {
									alias = joinField.toTableAlias();
								}
								if (field.getType().equals(String.class)) {
									condition.and().likeBoth(alias + "." + joinField.name(), obj.toString());
								} else {
									condition.and().is(alias + "." + joinField.name(), obj);
								}
							}
						}
					}
				}
			} catch (IllegalArgumentException | IllegalAccessException e) {
				e.printStackTrace();
			}
		}
		return condition;
	}

	public List<Criterion> getCriteria() {
		return criteria;
	}
}
